package com.zenithmind.common.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zenithmind.common.context.UserContext;
import com.zenithmind.common.metrics.BusinessMetrics;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * API访问日志切面
 * 记录所有API的访问日志和性能指标
 */
@Slf4j
@Aspect
@Component
@Order(2) // 在权限验证之后执行
@RequiredArgsConstructor
public class ApiAccessLogAspect {
    
    private final ObjectMapper objectMapper;
    private final BusinessMetrics businessMetrics;
    
    @Around("@within(org.springframework.web.bind.annotation.RestController)")
    public Object logApiAccess(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().toShortString();
        
        // 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
        
        // 构建请求日志
        ApiAccessLog accessLog = buildAccessLog(request, joinPoint);
        
        try {
            // 记录请求开始
            log.info("API请求开始: {}", accessLog.toRequestString());
            businessMetrics.recordApiRequest();
            
            // 执行目标方法
            Object result = joinPoint.proceed();
            
            // 计算执行时间
            long executionTime = System.currentTimeMillis() - startTime;
            accessLog.setExecutionTime(executionTime);
            accessLog.setSuccess(true);
            
            // 记录成功响应
            log.info("API请求成功: {}", accessLog.toResponseString());
            
            return result;
            
        } catch (Exception e) {
            // 计算执行时间
            long executionTime = System.currentTimeMillis() - startTime;
            accessLog.setExecutionTime(executionTime);
            accessLog.setSuccess(false);
            accessLog.setErrorMessage(e.getMessage());
            
            // 记录错误响应
            log.error("API请求失败: {}", accessLog.toResponseString(), e);
            businessMetrics.recordApiError();
            
            throw e;
        }
    }
    
    /**
     * 构建访问日志对象
     */
    private ApiAccessLog buildAccessLog(HttpServletRequest request, ProceedingJoinPoint joinPoint) {
        ApiAccessLog accessLog = new ApiAccessLog();
        
        if (request != null) {
            accessLog.setMethod(request.getMethod());
            accessLog.setUrl(request.getRequestURL().toString());
            accessLog.setUri(request.getRequestURI());
            accessLog.setQueryString(request.getQueryString());
            accessLog.setRemoteAddr(getClientIpAddress(request));
            accessLog.setUserAgent(request.getHeader("User-Agent"));
            accessLog.setHeaders(getRequestHeaders(request));
        }
        
        accessLog.setClassName(joinPoint.getTarget().getClass().getSimpleName());
        accessLog.setMethodName(joinPoint.getSignature().getName());
        accessLog.setArgs(getMethodArgs(joinPoint.getArgs()));
        
        // 获取用户信息
        try {
            String userId = UserContext.getUserId();
            String username = UserContext.getUsername();
            accessLog.setUserId(userId);
            accessLog.setUsername(username);
        } catch (Exception e) {
            log.debug("获取用户信息失败", e);
        }
        
        return accessLog;
    }
    
    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String[] headerNames = {
            "X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP",
            "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"
        };
        
        for (String headerName : headerNames) {
            String ip = request.getHeader(headerName);
            if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
                // 取第一个IP地址
                return ip.split(",")[0].trim();
            }
        }
        
        return request.getRemoteAddr();
    }
    
    /**
     * 获取请求头信息
     */
    private Map<String, String> getRequestHeaders(HttpServletRequest request) {
        Map<String, String> headers = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            // 过滤敏感头信息
            if (!isSensitiveHeader(headerName)) {
                headers.put(headerName, request.getHeader(headerName));
            }
        }
        
        return headers;
    }
    
    /**
     * 检查是否为敏感头信息
     */
    private boolean isSensitiveHeader(String headerName) {
        String lowerHeaderName = headerName.toLowerCase();
        return lowerHeaderName.contains("authorization") ||
               lowerHeaderName.contains("cookie") ||
               lowerHeaderName.contains("token") ||
               lowerHeaderName.contains("password");
    }
    
    /**
     * 获取方法参数（过滤敏感信息）
     */
    private String getMethodArgs(Object[] args) {
        if (args == null || args.length == 0) {
            return "[]";
        }
        
        try {
            // 简单处理，避免序列化复杂对象
            Object[] filteredArgs = Arrays.stream(args)
                    .map(this::filterSensitiveData)
                    .toArray();
            
            return objectMapper.writeValueAsString(filteredArgs);
        } catch (Exception e) {
            log.debug("序列化方法参数失败", e);
            return "[参数序列化失败]";
        }
    }
    
    /**
     * 过滤敏感数据
     */
    private Object filterSensitiveData(Object arg) {
        if (arg == null) {
            return null;
        }
        
        String argString = arg.toString();
        // 简单的敏感信息过滤
        if (argString.toLowerCase().contains("password") ||
            argString.toLowerCase().contains("token") ||
            argString.toLowerCase().contains("secret")) {
            return "[敏感信息已过滤]";
        }
        
        return arg;
    }
    
    /**
     * API访问日志数据类
     */
    private static class ApiAccessLog {
        private String method;
        private String url;
        private String uri;
        private String queryString;
        private String remoteAddr;
        private String userAgent;
        private Map<String, String> headers;
        private String className;
        private String methodName;
        private String args;
        private String userId;
        private String username;
        private long executionTime;
        private boolean success;
        private String errorMessage;
        
        // Getters and Setters
        public void setMethod(String method) { this.method = method; }
        public void setUrl(String url) { this.url = url; }
        public void setUri(String uri) { this.uri = uri; }
        public void setQueryString(String queryString) { this.queryString = queryString; }
        public void setRemoteAddr(String remoteAddr) { this.remoteAddr = remoteAddr; }
        public void setUserAgent(String userAgent) { this.userAgent = userAgent; }
        public void setHeaders(Map<String, String> headers) { this.headers = headers; }
        public void setClassName(String className) { this.className = className; }
        public void setMethodName(String methodName) { this.methodName = methodName; }
        public void setArgs(String args) { this.args = args; }
        public void setUserId(String userId) { this.userId = userId; }
        public void setUsername(String username) { this.username = username; }
        public void setExecutionTime(long executionTime) { this.executionTime = executionTime; }
        public void setSuccess(boolean success) { this.success = success; }
        public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
        
        public String toRequestString() {
            return String.format("[请求] %s %s | 用户: %s(%s) | IP: %s | 方法: %s.%s | 参数: %s",
                    method, uri, username, userId, remoteAddr, className, methodName, args);
        }
        
        public String toResponseString() {
            if (success) {
                return String.format("[响应] %s %s | 用户: %s | 耗时: %dms | 状态: 成功",
                        method, uri, username, executionTime);
            } else {
                return String.format("[响应] %s %s | 用户: %s | 耗时: %dms | 状态: 失败 | 错误: %s",
                        method, uri, username, executionTime, errorMessage);
            }
        }
    }
}
